

module uart_tx(

    //Transmit Data
    txd,

    //Uart Transmit Access Interface
    uart_tx_wr,
    uart_tx_data,
    uart_tx_busy,

    //Configuration
    uart_conf,

    clk_sys,
    rst_n

    );

//Transmit Data
output txd;

//Uart Transmit Access Interface
input        uart_tx_wr;
input  [7:0] uart_tx_data;
output       uart_tx_busy;

//Configuration
input [31:0] uart_conf; 

input clk_sys;
input rst_n;


//Configure Uart Transmit Parameter
wire [3:0] uart_baud_rate = uart_conf[3:0];
//   Conf. Value   Baud Rate
//       0x1       9600
//       0x2       19200
//       0x3       28800
//       0x4       38400
//       0x6       57600
//       0xC       115200
wire parity_check = uart_conf[4];
wire stop_bit     = uart_conf[8];

//System Clock is 100M
reg [15:0] baud_period;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_period <= 16'h0;
else
    begin
    case (uart_baud_rate)
        4'h1:    baud_period <= 16'h28B1; //10416.6667
        4'h2:    baud_period <= 16'h1458; //5208.3333
        4'h3:    baud_period <= 16'hD90;  //3472.2222
        4'h4:    baud_period <= 16'hA2C;  //2604.1667
        4'h6:    baud_period <= 16'h6C8;  //1736.1111
        4'hC:    baud_period <= 16'h365;  //868.5556
        default: baud_period <= 16'h28B1;
    endcase
    end
end


//Start Uart
reg uart_start;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    uart_start <= 1'b0;
else
    uart_start <= uart_tx_wr;
end


//Main FSM
reg [2:0] sta_uart;
localparam IDLE   = 3'h0,
           START  = 3'h1,
           WORK   = 3'h2,
           PARITY = 3'h3,
           STOP   = 3'h4,
           DONE   = 3'h5;

reg       baud_clk_fall;
reg [3:0] cnt_txd;
always @ (posedge clk_sys or negedge rst_n)
begin
if (rst_n == 1'b0)
    sta_uart <= IDLE;
else
    begin
    case (sta_uart)
        IDLE:
            if (uart_start == 1'b1) 
                sta_uart <= START;
        START:
            if (baud_clk_fall == 1'b1)
                sta_uart <= WORK;
        WORK:
            if (baud_clk_fall == 1'b1 && cnt_txd == 4'h7)
                sta_uart <= PARITY;
        PARITY:
            if (parity_check)
                begin
                if (baud_clk_fall == 1'b1)
                    sta_uart <= STOP;
                end
            else
                sta_uart <= STOP;
        STOP:
            if (stop_bit)
                begin
                if (baud_clk_fall == 1'b1)
                    sta_uart <= DONE;
                end            
            else
                sta_uart <= DONE;
        DONE:
            sta_uart <= IDLE;
        default:
            sta_uart <= IDLE;
    endcase
    end
end


//Uart Status
wire uart_work;
assign uart_work = (sta_uart == IDLE) ? 1'b0 : 1'b1;

reg uart_tx_busy;
always @ (posedge clk_sys)
    uart_tx_busy <= uart_work;


//Baud Clock
wire [15:0] half_baud_period;
assign half_baud_period = {1'b0,baud_period[15:1]};

reg [15:0] cnt_baud;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    cnt_baud <= 16'h0;
else 
    begin
    if (uart_work == 1'b1)
        begin
        if (cnt_baud >= baud_period)
            cnt_baud <= 16'h0;
        else
            cnt_baud <= cnt_baud + 16'h1;    
        end
    else
        cnt_baud <= 16'h0;
    end
end

//reg baud_clk_fall
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_clk_fall <= 1'b0;
else
    begin
    if (uart_work == 1'b1)
        baud_clk_fall <= (cnt_baud == baud_period) ? 1'b1 : 1'b0;
    end
end
reg baud_clk_rise;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_clk_rise <= 1'b0;
else
    begin
    if (uart_work == 1'b1)
        baud_clk_rise <= (cnt_baud == half_baud_period) ? 1'b1 : 1'b0;
    end
end


//Counter of Transmit Data
//reg [3:0] cnt_txd;
always @ (posedge clk_sys or negedge rst_n)
begin
if (rst_n == 1'b0)
    cnt_txd <= 4'h0;
else 
    begin
    if (sta_uart == WORK)
        begin
        if (baud_clk_fall == 1'b1)
            cnt_txd <= cnt_txd + 4'h1;
        end
    else 
        cnt_txd <= 4'h0;
    end
end


//Transmit Data Cache
reg [7:0] tx_data;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    tx_data <= 8'h0;
else
    begin
    if (sta_uart == WORK && baud_clk_fall == 1'b1) 
        tx_data <= {tx_data[6:0],1'b0};
    else 
        begin
        if (uart_tx_wr) 
            tx_data <= uart_tx_data;      
        end  
    end
end


//Parity Check
reg uart_txd_parity;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    uart_txd_parity <= 1'b0;
else
    begin 
    case (sta_uart)
        IDLE:
            uart_txd_parity <= 1'b0;
        START:
            uart_txd_parity <= ^tx_data;
    endcase
    end
end


//Transmit Uart Data
reg txd;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    txd <= 1'b0;
else 
    begin
    case (sta_uart)
        START:
            txd <= 1'b0;
        WORK:
            txd <= tx_data[7];
        PARITY:
            if (parity_check)
                txd <= uart_txd_parity;
        STOP:
            if (stop_bit)
                txd <= 1'b0;
        default: 
            txd <= 1'b1;
    endcase
    end
end


endmodule
